//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <iostream>
#include <fstream>

#include "libraries.h"
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
struct SmilesRef {

  char* smiles;
  char* ref;

  SmilesRef () {
    
    this->smiles = NULL;
    this->ref    = NULL;
    
  }

  SmilesRef (char* smilesIn) {
    
    if (this->smiles) delete[] this->smiles;
    this->smiles = new char[strlen(smilesIn) + 1];
    strcpy(this->smiles, smilesIn);

    this->ref = NULL;
    
  }

  SmilesRef (char* smilesIn, char* refIn) {
    
    if (this->smiles) delete[] this->smiles;
    this->smiles = new char[strlen(smilesIn) + 1];
    strcpy(this->smiles, smilesIn);

    if (this->ref) delete[] this->ref;
    this->ref = new char[strlen(refIn) + 1];
    strcpy(this->ref, refIn);
    
  }

  ~SmilesRef () {
    
    if (smiles) delete[] smiles;
    if (ref)    delete[] ref;
    
  }

  struct SmilesRef operator= (const struct SmilesRef &smilesRef) {

    if (this->smiles) delete[] this->smiles;
    this->smiles = new char[strlen(smilesRef.smiles) + 1];
    strcpy(this->smiles, smilesRef.smiles);

    if (this->ref) delete[] this->ref;
    this->ref = new char[strlen(smilesRef.ref) + 1];
    strcpy(this->ref, smilesRef.ref);

    return *this;

  };

};
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGTH_LINGO 4
#define LENGTHARRAY  4096

#define SMILESRef    struct SmilesRef
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
float obtainLingoSim (char*, char*);
float obtainLingoSimFast (char* string1, char* string2);
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int main (int argc, char** argv) {
  
  String* namefileIn      = NULL;
  String* namefileOut     = NULL;
  long    numberRelations = 0;

  if (argc < 4) {
    
    cout << "Error in the input parameters! Must by like: " << endl;
    cout << "./Relat.ive_ionLINGOsim <In Smiles File> <Out Relation LSim file (.gz)> <number of data>" << endl;
    
    return EXIT_FAILURE;
    
  }
  else {
    
    namefileIn      = new String(argv[1]);
    namefileOut     = new String(argv[2]);
    numberRelations = atoi(argv[3]);
    /*
    cout << "---------------------------------------------------------------------------------" << endl;
    cout << " Target Smiles File : " << argv[1] << endl;
    cout << "     DB Smiles File : " << argv[3] << endl;
    cout << "---------------------------------------------------------------------------------" << endl;
    */
  }
  
  fstream fileTarget, fileDB;
  
  fileTarget.open(namefileTargetSmilesFile->Out(), fstream::in);
  
  if (!fileTarget.is_open()) {
    
    if (namefileTargetSmilesFile) delete namefileTargetSmilesFile;
    if (namefileDBSmilesFile)     delete namefileDBSmilesFile;
    
    return EXIT_FAILURE;    
    
  }
  
  fileDB.open(namefileDBSmilesFile->Out(), fstream::in);
  
  if (!fileDB.is_open()) {
    
    fileTarget.close();
    
    if (namefileTargetSmilesFile) delete namefileTargetSmilesFile;
    if (namefileDBSmilesFile)     delete namefileDBSmilesFile;
    
    return EXIT_FAILURE;    
    
  }
  
  TListE <SMILESRef>* listTarget = new TListE <SMILESRef> ();
  
  char                buffer [LENGTHARRAY + 1], *smileRead, *refRead;
  
  while (!fileTarget.eof()) {
    
    fileTarget.getline(buffer, LENGTHARRAY);
    if (!strlen(buffer)) continue;
    
    if ((smileRead = strtok(buffer, " \t\n\r"))) 
      listTarget->Add(new SMILESRef (smileRead));
    
  }
  
  while (!fileDB.eof()) {
    
    fileDB.getline(buffer, LENGTHARRAY);
    
    if ((!strlen(buffer))                          ||
        (!(smileRead = strtok(buffer, " \t\n\r"))) ||
        (!(refRead = strtok(NULL, " \t\n\r"))))     
      continue;
    
    printf("%s %s", smileRead, refRead);
    
    listTarget->SetInitial();
    while (listTarget->SetNext()) 
      printf(" %.6f", obtainLingoSim(listTarget->GetCurrent()->smiles, smileRead));
    
    printf("\n");
     
  }
  
  fileDB.close();
  
  if (listTarget)               delete listTarget;
  
  if (namefileTargetSmilesFile) delete namefileTargetSmilesFile;
  if (namefileDBSmilesFile)     delete namefileDBSmilesFile;
  
  
  return EXIT_SUCCESS;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
float obtainLingoSim (char *string1, char *string2) {

  long length1 = strlen(string1);
  long length2 = strlen(string2);
  long i;
  long j;
  long k;
  long z;
  long higher = ((length1 > length2)?length1:length2);

  char tmpString1 [length1 + 1];
  char tmpString2 [length2 + 1];
  char compare    [LENGTH_LINGO + 1];

  for (i = 0, j = 0, k = 0; i < higher; i++) {
    if (i < length1) {
      if      (string1[i] == 'l') tmpString1[j - 1] = 'L';
      else if (string1[i] == 'r') tmpString1[j - 1] = 'R';
      else                        tmpString1[j++]   = string1[i];
    }
    if (i < length2) {
      if      (string2[i] == 'l') tmpString2[k - 1] = 'L';
      else if (string2[i] == 'r') tmpString2[k - 1] = 'R';
      else                        tmpString2[k++]   = string2[i];
    }
  }
  
  tmpString1[j] = '\0'; 
  tmpString2[k] = '\0';
  
  j -= (LENGTH_LINGO - 1); 
  k -= (LENGTH_LINGO - 1);
  
  long   L               = j + k; 
  long   zHigher         = ((j > k)?j:k);

  float  similarityValue = 0;
  float  Nai;
  float  Nbi;
  
  bool   analize;

  for (i = 0; i < j + k; i++) {

    analize = true;

    Nai = 0;
    Nbi = 0;

    if (i < j) {

      memcpy(compare, tmpString1 + i, LENGTH_LINGO);

      for (z = 0; z < zHigher; z++) {
        if (z < j && !memcmp(compare, tmpString1 + z, LENGTH_LINGO)) {
          if (z < i) {
            analize = false;
            break;
          }
          Nai++;
        }
        if (z < k && !memcmp(compare, tmpString2 + z, LENGTH_LINGO)) {
          Nbi++;
        }
      }
    }
    else {

      memcpy(compare, tmpString2 + i - j, LENGTH_LINGO);

      for (z = 0; z < zHigher; z++) {
        if (z < j && !memcmp(compare, tmpString1 + z, LENGTH_LINGO)) {
          analize = false;
          break;
        }
        if (z < k && !memcmp(compare, tmpString2 + z, LENGTH_LINGO)) {
          if (z < i - j) {
            analize = false;
            break;
          }
          Nbi++;
        }
      }
    }

    if (analize == true) similarityValue += ((((Nai - Nbi) >= 0)?((Nai - Nbi)):((Nbi - Nai)))/(Nai + Nbi));
    else                 L--;
  }

  return 1 - similarityValue/L;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
float obtainLingoSimFast (char* string1, char* string2) {

  char  tmpString1 [strlen(string1) + 1];
  char  tmpString2 [strlen(string2) + 1];
  char  compare   [LENGTH_LINGO + 1];
  
  long  i;
  long  j                  = strlen(string1) - (LENGTH_LINGO - 1);;
  long  k                  = strlen(string2) - (LENGTH_LINGO - 1);
  long  z;
  long  L                  = j + k; 
  long  zHigher            = ((j > k)?j:k);
  
  float similarityValue    = 0;
  float Nai;
  float Nbi;
  
  bool   analize;

  for (i = 0; i < j + k; i++) {

    analize = true;

    Nai = 0; 
    Nbi = 0;

    if (i < j) {

      memcpy(compare, tmpString1 + i, LENGTH_LINGO);

      for (z = 0; z < zHigher; z++) {
        if (z < j && !memcmp(compare, tmpString1 + z, LENGTH_LINGO)) {
          if (z < i) {
            analize = false;
            break;
          }
          Nai++;
        }
        if (z < k && !memcmp(compare, tmpString2 + z, LENGTH_LINGO)) {
          Nbi++;
        }
      }
    }
    else {

      memcpy(compare, tmpString2 + i - j, LENGTH_LINGO);

      for (z = 0; z < zHigher; z++) {
        if (z < j && !memcmp(compare, tmpString1 + z, LENGTH_LINGO)) {
          analize = false;
          break;
        }
        if (z < k && !memcmp(compare, tmpString2 + z, LENGTH_LINGO)) {
          if (z < i - j) {
            analize = false;
            break;
          }
          Nbi++;
        }
      }
    }

    if (analize == true) similarityValue += ((((Nai - Nbi) >= 0)?((Nai - Nbi)):((Nbi - Nai)))/(Nai + Nbi));
    else                 L--;
  }

  return 1 - similarityValue/L;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
